import java.io.*;
import java.net.*;
import java.util.*;

import xchange.Debug;

/**
 * This class implements a separate application (server process) handles the
 * GET, POST and REMOVE commands blocks are stored in ./files directory
 */

public class StorageServer {

	private String ip = "";
	private final int port = 9002;
	private final int BLOCKSIZE = 64 * 1024;

	// un-buffered socket stream (flushing has no purpose)
	InputStream dis;
	OutputStream dos;

	// buffered socket stream
	BufferedReader in;
	PrintWriter out;

	public StorageServer() {
		if (Debug.DEBUG) {
			System.out.println("Storageserver started ...");
		}
		// check if ./files directory exists
		File file = new File("./files");
		if (!file.exists()) {
			System.out
					.println("Please create directory ./files with r+w access.");
			System.err.println("ERROR : directory ./files does not exist.");
			System.exit(0);
		}

		try {
			listenAndHandle();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * create and listen on server socket
	 * 
	 */

	public void listenAndHandle() throws Exception {
		ServerSocket ss = new ServerSocket(port);

		// listen for incoming connections

		while (true) {
			Socket cs = ss.accept();
			System.out.println("client connected");
			// handle a single request
			handle(cs);
			// close client socket and resume listening
			cs.close();
		}
	}

	/**
	 * handle incoming messages all messages are strings ending on "\n"
	 */
	private void handle(Socket s) throws Exception {
		// get socket i/o stream
		dis = s.getInputStream();
		dos = s.getOutputStream();

		// open a BufferedReader on it
		in = new BufferedReader(new InputStreamReader(dis));
		// open a PrintWriter on it
		out = new PrintWriter(dos, true);
		
		// get the message
		String line = null;	
		do {
			line = in.readLine();
		} while (line == null);
		if (line.startsWith("POST")) {
			handlePost(line);
		}

		else if (line.startsWith("GET")) {
			handleGet(line);
		}

		else if (line.startsWith("REMOVE")) {
			handleRemove(line);
		}

		else {
			handleError();
		}
	}
/**
 * This method will handle incoming data, and try's to add in a new or existing file with a giving name.
 * @param line
 * @throws Exception
 */
	private void handlePost(String line) throws Exception {
		if (Debug.DEBUG) {
			System.out.println("Storageserver received : " + line);		
		}
		RandomAccessFile raf;
		String splittedLine[] = line.split(" "); //formatted splittedLine = [0]=action & [1] =filename & [2] =size &[3]blocknr & [4]blocks
		
		try {
			//just to check if the file exists, if not i can pre-allocate the space needed.
			File ftest = new File("files"+File.separator+splittedLine[1]);
			if (!ftest.exists()) {
				//try make the file, otherwise it will return the existing file with the same name.
				 raf = new RandomAccessFile("files"+File.separator+splittedLine[1], "rw");
				// all bytes are set to zero by default
				raf.setLength(Long.parseLong(splittedLine[2]));
			}
			else{
				//try make the file, otherwise it will return the existing file with the same name.
				raf = new RandomAccessFile("files"+File.separator+splittedLine[1], "rw");
			}
			//start of the filepointer is (blocknr * BLOCKSIZE) 
			long start = (Integer.parseInt(splittedLine[3])*BLOCKSIZE);
			// Seek to the blocknr offset
			raf.seek(start);			
			//append blocks to the end of file
			raf.writeBytes(splittedLine[4]);
			raf.close();
		}
		catch (IOException e)
		{
			System.out.println("RandomAccesFile Exception ");
		}
		//out.println("OK");
		if (Debug.DEBUG) {
			System.out.println("Storageserver : added block to file");
		}
	}
/**
 * This method will search the requested file, and return the requested block.
 * @param line
 * @throws Exception
 */
	private void handleGet(String line) throws Exception {
		if (Debug.DEBUG) {
			System.out.println("Storageserver received : " + line);		
		}
		String splittedLine[] = line.split(" "); //formatted splittedLine = [0]=action & [1] =filename & [2] =blocknr
		byte[] buffer = new byte[BLOCKSIZE];
		File[] files = new File("./files").listFiles();
		//check on no files
		if (files == null) {
			System.err
					.println("ERROR : directory xchange/shared does not exist.");
			return;
		}
		//find the file
		for (int i = 0; i < files.length; i++) {
			//get the block from file
			//System.out.println("found file: "+files[i].getName());
			if (files[i].getName().equals(splittedLine[1])){
				RandomAccessFile raf = new RandomAccessFile(files[i], "r");
				//seek to start block number
				long start = (Integer.parseInt(splittedLine[2])*BLOCKSIZE);
				raf.seek(start);
				int value = raf.read(buffer);//will read from seek(start) till the buffer is completely filled.
				raf.close();
				//send bugger back to client
			    for(int j =0; j <(buffer.length);j++){
			    	out.write(buffer[j]);
			    	
			    }
			   
			    out.flush();
			    System.out.println("send block "+splittedLine[2]+"from file: "+splittedLine[1]);	
			
			}
		}
		if (Debug.DEBUG) {
			System.out.println("Storageserver : sent block from file");
		}
	}
/**
 * This method will remove the specified file from the ./files bin.
 * @param line
 * @throws Exception
 */
	private void handleRemove(String line) throws Exception {
		if (Debug.DEBUG) {
			System.out.println("Storageserver received : " + line);
		}
		// your code here
		String splittedLine[] = line.split(" "); //formatted splittedLine = [0]=action & [1] =filename 
		File[] files = new File("./files").listFiles();
		//find the file
		for (int i = 0; i < files.length; i++) {
			//get the block from file
			if (files[i].getName().equals(splittedLine[1])){
				//remove file
				System.out.println("removed: "+splittedLine[1]);
				files[i].delete();
			}
			}
		//out.println("OK");
		if (Debug.DEBUG) {
			System.out.println("Storageserver : all files removed !");
		}
	}

	private void handleError() throws Exception {
		out.println("FAIL");
	}

	public static void main(String[] args) throws Exception {
		new StorageServer();
	}
}
